#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <pthread.h>

#include "task.hpp"

using namespace std;

const int default_threads = 5;

struct thread_info {
    pthread_t _id;
    string _name;
};

template<class T>
class thread_pool {
public:
    static thread_pool<T>* get_instance() {
        // 多线程访问这一个单例对象需要保证互斥
        if(_self == nullptr) {
            // 互斥
            // 也可以只要一个判断，但是效率会降低
            // 因为只有第一次调用时会加锁
            // 后续在调用其实是不用加锁的，一个判断的话会让多线程并发获得单例对象变成串行获得
            // 这样效率会降低，所以使用两次判断即可解决
            pthread_mutex_lock(&_lock);
            if(_self == nullptr) {
                _self = new thread_pool<T>;
                cout << "Singleton is created" << endl;
            }
            pthread_mutex_unlock(&_lock);
        }
        return _self;
    }

    // 需要注意的是，线程要执行的函数只允许有一个void*类型的参数
    // 如果在类中定义需要声明为静态成员函数
    // 这样就不需要传递this指针
    // 否则默认传递的this会抢占这个参数，也就是会有两个参数，这不符合要求
    static void *handle_task(void *args) {
        // 由于没有this指针，所以就无法访问类中的非静态成员
        // 所以在创建线程时，可以把该对象的地址，也就是this指针作为该函数的参数传递过来
        // 这样在线程中就可以使用了
        thread_pool<T> *tp = static_cast<thread_pool<T> *>(args);
        while(true) {
            // 取出任务
            // 访问共享队列先加锁
            tp->lock();
            while(tp->task_queue_empty()) {
                tp->wait();
            }
            T t = tp->pop();
            tp->unlock();
            // 处理任务
            t.run();
            close(t.get_sockfd());
        }
        return nullptr;
    }

    bool task_queue_empty() const {
        return _q.empty();
    }

    void lock() {
        pthread_mutex_lock(&_mutex);
    }

    void unlock() {
        pthread_mutex_unlock(&_mutex);
    }

    void wait() {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void signal() {
        pthread_cond_signal(&_cond);
    }

    void push(const T &v) {
        // 保证任何时刻只有一个线程在操作任务队列
        lock();
        _q.push(v);
        unlock();

        // 此时有任务了可以唤醒等待的一个线程
        signal();
    }

    T pop() {
        T v = _q.front();
        _q.pop();
        return v;
    }

private:
    thread_pool(int nums_of_threads = default_threads) :_tds(nums_of_threads) {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for(int i = 0; i < _tds.size(); ++i) {
            _tds[i]._name = "thread_" + to_string(i);
            pthread_create(&_tds[i]._id, nullptr, handle_task, this);
        }
    }
    ~thread_pool() {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for(int i = 0; i < _tds.size(); ++i) {
            pthread_join(_tds[i]._id, nullptr);
        }
    }

    thread_pool(const thread_pool<T>& _tds) = delete;
    thread_pool<T>& operator=(const thread_pool<T>& _tds) = delete;

private:
    vector<thread_info> _tds;
    queue<T> _q;

    //保证线程间访问队列的互斥性
    pthread_mutex_t _mutex;
    // 当任务队列为空时，需要让线程们等待任务的产生
    // 所以需要条件变量，当条件不满足时让其在条件变量下等待
    // 当有任务时通过主线程唤醒
    pthread_cond_t _cond;
    static thread_pool<T> *_self;
    static pthread_mutex_t _lock;

};

template<class T>
thread_pool<T> *thread_pool<T>::_self = nullptr;

template<class T>
pthread_mutex_t thread_pool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;
